<?php

declare(strict_types=1);

namespace app\service\org;

use app\model\Org;
use app\model\OrgAddress;
use app\model\OrgContact;
use app\model\OrgProfile;
use app\model\Region;
use app\model\User;
use InvalidArgumentException;
use think\Collection;
use think\facade\Db;
use think\Paginator;

class OrganizationService
{
    public function paginate(array $filters, int $page, int $pageSize): Paginator
    {
        $query = Org::with(['profile', 'contact', 'addresses', 'region'])
            ->order('id', 'desc');

        if (!empty($filters['keyword'])) {
            $keyword = trim((string) $filters['keyword']);
            $query->whereLike('name', '%' . $keyword . '%');
        }

        if (!empty($filters['org_type'])) {
            $query->where('org_type', trim((string) $filters['org_type']));
        }

        if (isset($filters['status']) && $filters['status'] !== '') {
            $query->where('status', (int) $filters['status']);
        }

        if (!empty($filters['region_id'])) {
            $query->where('region_id', (int) $filters['region_id']);
        }

        if (!empty($filters['invited_by'])) {
            $query->where('inviter_user_id', (int) $filters['invited_by']);
        } elseif (!empty($filters['inviter_user_id'])) {
            $query->where('inviter_user_id', (int) $filters['inviter_user_id']);
        }

        if (!empty($filters['owner_user_id'])) {
            $query->where('owner_user_id', (int) $filters['owner_user_id']);
        }

        if (!empty($filters['ids']) && is_array($filters['ids'])) {
            $query->whereIn('id', array_unique(array_map('intval', $filters['ids'])));
        }

        return $query->paginate([
            'list_rows' => $pageSize,
            'page' => $page,
        ]);
    }

    public function all(array $filters = []): Collection
    {
        $query = Org::with(['profile', 'contact', 'addresses', 'region'])
            ->order('id', 'desc');

        if (!empty($filters['status'])) {
            $query->where('status', (int) $filters['status']);
        }

        if (!empty($filters['org_type'])) {
            $query->where('org_type', trim((string) $filters['org_type']));
        }

        return $query->select();
    }

    public function findById(int $id): ?Org
    {
        return Org::with(['profile', 'contact', 'addresses', 'region'])->find($id);
    }

    public function create(array $payload): Org
    {
        [$data, $profile, $contact, $addresses] = $this->preparePayload($payload, true);

        /** @var Org $org */
        $org = Db::transaction(function () use ($data, $profile, $contact, $addresses) {
            /** @var Org $org */
            $org = Org::create($data);
            $this->syncProfile($org, $profile);
            $this->syncContact($org, $contact);
            $this->syncAddresses($org, $addresses);
            return $org;
        });

        return $this->findById((int) $org->id);
    }

    public function update(Org $org, array $payload): Org
    {
        [$data, $profile, $contact, $addresses] = $this->preparePayload($payload, false, $org);

        Db::transaction(function () use ($org, $data, $profile, $contact, $addresses) {
            if ($data !== []) {
                $org->save($data);
            }
            if ($profile !== null) {
                $this->syncProfile($org, $profile);
            }
            if ($contact !== null) {
                $this->syncContact($org, $contact);
            }
            if ($addresses !== null) {
                $this->syncAddresses($org, $addresses);
            }
        });

        return $this->findById((int) $org->id);
    }

    public function delete(Org $org): bool
    {
        return (bool) Db::transaction(function () use ($org) {
            OrgProfile::where('org_id', $org->id)->delete();
            OrgContact::where('org_id', $org->id)->delete();
            OrgAddress::where('org_id', $org->id)->delete();
            return $org->delete();
        });
    }

    public function approve(Org $org, int $adminId, string $remark = ''): Org
    {
        if ((int) $org->status === Org::STATUS_ACTIVE) {
            throw new InvalidArgumentException('组织已审核通过');
        }

        return Db::transaction(function () use ($org, $adminId, $remark) {
            $extra = $this->mergeExtra($org, [
                'audit' => [
                    'result' => 'approved',
                    'remark' => $remark,
                ],
            ]);

            $org->save([
                'status' => Org::STATUS_ACTIVE,
                'approved_by' => $adminId,
                'approved_at' => date('Y-m-d H:i:s'),
                'extra' => $extra,
            ]);

            return $this->findById((int) $org->id);
        });
    }

    public function reject(Org $org, int $adminId, string $remark): Org
    {
        if ($remark === '') {
            throw new InvalidArgumentException('驳回原因不能为空');
        }

        if ((int) $org->status === Org::STATUS_DISABLED) {
            throw new InvalidArgumentException('组织已处于禁用状态');
        }

        return Db::transaction(function () use ($org, $adminId, $remark) {
            $extra = $this->mergeExtra($org, [
                'audit' => [
                    'result' => 'rejected',
                    'remark' => $remark,
                ],
            ]);

            $org->save([
                'status' => Org::STATUS_DISABLED,
                'approved_by' => $adminId,
                'approved_at' => date('Y-m-d H:i:s'),
                'extra' => $extra,
            ]);

            return $this->findById((int) $org->id);
        });
    }

    public function format(Org $org): array
    {
        $extra = $org->extra;
        if (!is_array($extra)) {
            $extra = $extra ? (array) json_decode((string) $extra, true) : [];
        }
        if (!is_array($extra)) {
            $extra = [];
        }

        return [
            'id' => (int) $org->id,
            'name' => $org->name,
            'org_type' => $org->org_type,
            'logo_url' => $org->logo_url,
            'warehouse_code' => $org->warehouse_code,
            'region_id' => $org->region_id ? (int) $org->region_id : null,
            'owner_user_id' => $org->owner_user_id ? (int) $org->owner_user_id : null,
            'invited_by' => $org->inviter_user_id ? (int) $org->inviter_user_id : null,
            'inviter_user_id' => $org->inviter_user_id ? (int) $org->inviter_user_id : null,
            'approved_by' => $org->approved_by ? (int) $org->approved_by : null,
            'approved_at' => $org->approved_at,
            'status' => (int) $org->status,
            'status_label' => $this->statusLabel((int) $org->status),
            'extra' => $extra,
            'create_time' => $org->create_time,
            'update_time' => $org->update_time,
            'profile' => $org->profile ? $org->profile->toArray() : null,
            'contact' => $org->contact ? $org->contact->toArray() : null,
            'addresses' => $org->addresses ? array_map(static fn($item) => $item->toArray(), $org->addresses->all()) : [],
            'region' => $org->region ? [
                'id' => (int) $org->region->id,
                'name' => $org->region->name,
                'code' => $org->region->code,
            ] : null,
        ];
    }

    public function formatCollection(iterable $orgs): array
    {
        $results = [];
        foreach ($orgs as $org) {
            if ($org instanceof Org) {
                $results[] = $this->format($org);
            }
        }

        return $results;
    }

    private function preparePayload(array $payload, bool $isCreate, ?Org $org = null): array
    {
        $data = [];

        if ($isCreate || array_key_exists('name', $payload)) {
            $name = trim((string) ($payload['name'] ?? ''));
            if ($name === '') {
                throw new InvalidArgumentException('组织名称不能为空');
            }
            $data['name'] = $name;
        }

        if ($isCreate || array_key_exists('org_type', $payload)) {
            $orgType = trim((string) ($payload['org_type'] ?? ''));
            if (!in_array($orgType, [Org::TYPE_SELLER, Org::TYPE_BUYER, Org::TYPE_BOTH], true)) {
                throw new InvalidArgumentException('组织类型不正确');
            }
            $data['org_type'] = $orgType;
        }

        if ($isCreate || array_key_exists('logo_url', $payload)) {
            $data['logo_url'] = $payload['logo_url'] ?? null;
        }

        if (
            $isCreate
            || array_key_exists('invited_by', $payload)
            || array_key_exists('inviter_user_id', $payload)
        ) {
            $value = $payload['invited_by'] ?? $payload['inviter_user_id'] ?? null;
            $data['inviter_user_id'] = ($value !== null && $value !== '') ? (int) $value : null;
        }

        if ($isCreate || array_key_exists('warehouse_code', $payload)) {
            $data['warehouse_code'] = isset($payload['warehouse_code'])
                ? trim((string) $payload['warehouse_code'])
                : '';
        }

        if ($isCreate || array_key_exists('owner_user_id', $payload)) {
            $owner = $payload['owner_user_id'] ?? null;
            if ($owner !== null && $owner !== '') {
                $ownerId = (int) $owner;
                if ($ownerId <= 0 || User::where('id', $ownerId)->count() === 0) {
                    throw new InvalidArgumentException('负责人用户不存在');
                }
                $data['owner_user_id'] = $ownerId;
            } else {
                $data['owner_user_id'] = null;
            }
        }

        if ($isCreate || array_key_exists('status', $payload)) {
            $status = (int) ($payload['status'] ?? Org::STATUS_PENDING);
            if (!in_array($status, [Org::STATUS_PENDING, Org::STATUS_ACTIVE, Org::STATUS_DISABLED], true)) {
                throw new InvalidArgumentException('组织状态不正确');
            }
            $data['status'] = $status;
        }

        if ($isCreate || array_key_exists('region_id', $payload)) {
            $regionId = $payload['region_id'] ?? null;
            if ($regionId !== null && $regionId !== '') {
                $regionId = (int) $regionId;
                if ($regionId > 0 && Region::where('id', $regionId)->count() === 0) {
                    throw new InvalidArgumentException('所属区域不存在');
                }
                $data['region_id'] = $regionId > 0 ? $regionId : null;
            } else {
                $data['region_id'] = null;
            }
        }

        if ($isCreate || array_key_exists('approved_by', $payload)) {
            $approvedBy = $payload['approved_by'] ?? null;
            $data['approved_by'] = ($approvedBy !== null && $approvedBy !== '')
                ? (int) $approvedBy
                : null;
        }

        if ($isCreate || array_key_exists('approved_at', $payload)) {
            $approvedAt = $payload['approved_at'] ?? null;
            $data['approved_at'] = $approvedAt !== '' ? $approvedAt : null;
        }

        if ($isCreate || array_key_exists('extra', $payload)) {
            $extra = $payload['extra'] ?? [];
            if ($extra !== null && !is_array($extra)) {
                throw new InvalidArgumentException('扩展信息格式不正确');
            }
            $data['extra'] = $extra ?? [];
        }

        $profile = null;
        if ($isCreate || array_key_exists('profile', $payload)) {
            $profileValue = $payload['profile'] ?? [];
            if ($profileValue !== null && !is_array($profileValue)) {
                throw new InvalidArgumentException('扩展资料格式不正确');
            }
            $profile = $profileValue ?? [];
        }

        $contact = null;
        if ($isCreate || array_key_exists('contact', $payload)) {
            $contactValue = $payload['contact'] ?? [];
            if ($contactValue !== null && !is_array($contactValue)) {
                throw new InvalidArgumentException('联系信息格式不正确');
            }
            $contact = $contactValue ?? [];
        }

        $addresses = null;
        if ($isCreate || array_key_exists('addresses', $payload)) {
            $addressesValue = $payload['addresses'] ?? [];
            if ($addressesValue !== null && !is_array($addressesValue)) {
                throw new InvalidArgumentException('地址信息格式不正确');
            }

            $addresses = [];
            if (is_array($addressesValue)) {
                foreach ($addressesValue as $index => $item) {
                    if (!is_array($item)) {
                        throw new InvalidArgumentException(sprintf('第 %d 条地址格式不正确', $index + 1));
                    }
                    $addresses[] = $item;
                }
            }
        }

        if (!$isCreate) {
            $data = $data === [] ? [] : $data;
        }

        return [$data, $profile, $contact, $addresses];
    }

    private function syncProfile(Org $org, ?array $profile): void
    {
        if ($profile === null) {
            return;
        }

        $record = OrgProfile::where('org_id', $org->id)->find();
        $profile['org_id'] = $org->id;

        if ($record) {
            $record->save($profile);
        } else {
            OrgProfile::create($profile);
        }
    }

    private function syncContact(Org $org, ?array $contact): void
    {
        if ($contact === null) {
            return;
        }

        $record = OrgContact::where('org_id', $org->id)->find();
        $contact['org_id'] = $org->id;

        if ($record) {
            $record->save($contact);
        } else {
            OrgContact::create($contact);
        }
    }

    private function syncAddresses(Org $org, ?array $addresses): void
    {
        if ($addresses === null) {
            return;
        }

        OrgAddress::where('org_id', $org->id)->delete();

        if ($addresses === []) {
            return;
        }

        $records = [];
        foreach ($addresses as $item) {
            $item['org_id'] = $org->id;
            $item['is_default'] = isset($item['is_default']) ? (int) (bool) $item['is_default'] : 0;
            $records[] = $item;
        }

        (new OrgAddress())->saveAll($records);
    }

    private function mergeExtra(Org $org, array $extra): array
    {
        $current = $org->extra;
        if (!is_array($current)) {
            $current = $current ? (array) json_decode((string) $current, true) : [];
        }
        if (!is_array($current)) {
            $current = [];
        }

        return array_replace_recursive($current, $extra);
    }

    private function statusLabel(int $status): string
    {
        return match ($status) {
            Org::STATUS_ACTIVE => '已启用',
            Org::STATUS_DISABLED => '已禁用',
            default => '待审核',
        };
    }
}
